import type { Directive, DirectiveBinding } from 'vue'

interface IOptions {
  /** 动画距离 */
  distance?: number
  /** 动画时长 */
  duration?: number
  /** 动画时间曲线 */
  easing?: string
}

const map = new WeakMap()

const ob = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      const animation = map.get(entry.target)
      if (animation) {
        animation.play()
        ob.unobserve(entry.target)
      }
    }
  }
})

function isBelowViewport(el: HTMLElement) {
  const rect = el.getBoundingClientRect()
  return rect.top > window.innerHeight
}

/**
 * #### 滑入动画,当元素进入视口时触发
 */
const slideIn: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding<IOptions | null>) {
    const {
      distance = 100,
      duration = 500,
      easing = 'ease-out'
    } = binding.value || {}

    if (!isBelowViewport(el)) {
      return
    }

    const animation = el.animate(
      [
        { transform: `translateY(${distance}px)`, opacity: 0.5 },
        { transform: 'translateY(0)', opacity: 1 }
      ],
      {
        duration,
        easing,
        fill: 'forwards'
      }
    )

    animation.pause()
    map.set(el, animation)
    ob.observe(el)
  },

  unmounted(el: HTMLElement) {
    ob.unobserve(el)
  }
}

export default slideIn
